/*
 * nlsync
 * Copyright (c) 2010 apechurin 
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;

namespace nlsync.Xls
{
    /// <summary>
    /// A Xls based implementation of the IStringBundle interface.
    /// </summary>
    class XlsStringBundle : IStringBundle
    {
        #region StringEnumerator class

        /// <summary>
        /// The strings enumerator class.
        /// </summary>
        private class StringEnumerator : IEnumerator<IStringInfo>
        {
            #region Constructor

            /// <summary>
            /// Constructor.
            /// </summary>
            /// <param name="bundle">The parent bundle.</param>
            public StringEnumerator(XlsStringBundle bundle)
            {
                mBundle = bundle;
                Reset();
                mBundle.removeListener += RemoveRowListener;
            }

            #endregion

            #region IEnumerator methods

            public IStringInfo Current
            {
                get
                {
                    if (mCur == null)
                        mCur = new XlsStringInfo(mCurRow, mBundle.Catalog.Sheet);
                    return mCur;
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    return (this as IEnumerator<IStringInfo>).Current;
                }
            }

            public bool MoveNext()
            {
                XlsSheet sheet = mBundle.mCatalog.Sheet;

                mCur = null;

                // while there are more rows
                while (!mDone)
                {
                    // move to the next row
                    mCurRow++;

                    // if it's a last row then remember this fact
                    if (mCurRow >= sheet.RowsCount)
                    {
                        mDone = true;
                        if (mCurRow > sheet.RowsCount)
                            break;
                    }

                    // is this our row?
                    string value = sheet.GetValue(mCurRow, 1);
                    if (value == mBundle.mName)
                        return true;
                }

                return false;
            }

            public void Reset()
            {
                mCurRow = 0;
                mDone = false;
                mCur = null;
            }

            public void Dispose()
            {
                mBundle.removeListener -= RemoveRowListener;
            }

            #endregion

            /// <summary>
            /// Called when a row is removed.
            /// </summary>
            /// <param name="row"></param>
            private void RemoveRowListener(int row)
            {
                if (!mDone && mCurRow > 0 && mCurRow >= row)
                    mCurRow--;
            }

            #region Data fields

            /// <summary>
            /// The parent bundle.
            /// </summary>
            XlsStringBundle mBundle;

            /// <summary>
            /// The current row.
            /// </summary>
            int mCurRow;
            
            /// <summary>
            /// Stores the last value returned by Current.
            /// </summary>
            IStringInfo mCur;
            
            /// <summary>
            /// If true then enumeration completed.
            /// </summary>
            bool mDone;

            #endregion
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="name">The name of the bundle. It's a column A on a 
        ///     sheet.</param>
        /// <param name="catalog">The parent catalog.</param>
        public XlsStringBundle(string name, XlsStringCatalog catalog)
        {
            mName = name;
            mCatalog = catalog;
        }

        #endregion

        #region IEnumerable methods

        public IEnumerator<IStringInfo> GetEnumerator()
        {
            return new StringEnumerator(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new StringEnumerator(this);
        }

        #endregion

        #region IStringBundle methods

        public IStringInfo Find(string name)
        {
            XlsSheet sheet = Catalog.Sheet;
            for (int i = 1; i <= sheet.RowsCount; i++)
            {
                if (sheet.GetValue(i, XlsStringInfo.NameColumn) == name &&
                      sheet.GetValue(i, XlsStringInfo.SourceColumn) == mName)
                    return new XlsStringInfo(i, sheet);
            }

            return null;
        }

        public IStringInfo Add(string name)
        {
            IStringInfo str = new XlsStringInfo(Catalog.Sheet, mName, name);
            return str;
        }

        public void Remove(IStringInfo str)
        {
            XlsStringInfo xstr = str as XlsStringInfo;
            int row = xstr.Remove();
            if (removeListener != null)
                removeListener(row);
        }

        public bool IsCommentSupported
        {
            get
            {
                return true;
            }
        }

        public void Close()
        {
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a catalog.
        /// </summary>
        XlsStringCatalog Catalog
        {
            get
            {
                return mCatalog;
            }
        }
        private XlsStringCatalog mCatalog;

        #endregion

        #region Implementation

        /// <summary>
        /// The delegate which defines a callback which is called when a row
        /// is removed.
        /// </summary>
        /// <param name="row"></param>
        private delegate void RemoveListener(int row);

        /// <summary>
        /// The event that is used to notify when a row is removed.
        /// </summary>
        private event RemoveListener removeListener; 

        #endregion

        #region Data fields

        private string mName;

        #endregion
    }
}
